package com.lxm.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lxm.common.contants.RedisExpire;
import com.lxm.common.contants.UserConstants;
import com.lxm.common.tools.LogicDeleteUtils;
import com.lxm.common.tools.RegexUtils;
import com.lxm.common.tools.UserHolder;
import com.lxm.dto.PasswordEditDTO;
import com.lxm.dto.Result;
import com.lxm.dto.UserInfoDTO;
import com.lxm.dto.UserInfoModifyDTO;
import com.lxm.entity.Login;
import com.lxm.entity.UserInfo;
import com.lxm.entity.UserInfoLogic;
import com.lxm.mapper.UserInfoDao;
import com.lxm.service.LoginService;
import com.lxm.service.UserInfoService;
import com.lxm.service.logicService.UserInfoLogicService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2024-10-02
 */
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoDao, UserInfo> implements UserInfoService {

    @Resource
    private LoginService loginService;
    @Resource
    private UserInfoLogicService userInfoLogicService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //在注册时必须录入自己的关键完整信息，如果不录入直接回滚拒绝注册。
    @Override
    public Result register(UserInfoDTO userInfoDTO) {
        //前端先进行必填是否填写完毕，并且检查邮箱是否填写，填写才用JSON发送过来，不然JSON中不应当有email字段
        //正常用户，判断手机号是否已注册,这里只需要知道这个号是否被注册，不要返回，优化下性能
        if (loginService.exists(new LambdaQueryWrapper<Login>()
                .eq(Login::getPhone,
                        userInfoDTO.getPhone()))) return Result
                .fail(UserConstants.USER_PHONE_REGISTER_EXIST);
        if (exists(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getEmail,
                        userInfoDTO.getEmail()))) return Result
                .fail(UserConstants.USER_EMAIL_REGISTER_EXIST);
        //这里要让前段提出选项，让前段给出用户选择是否愿意为昵称添加随机后缀，由前端进行昵称生成操作
        if (loginService.exists(new LambdaQueryWrapper<Login>()
                .eq(Login::getNickName, userInfoDTO.getNickName()))) return Result
                .fail(UserConstants.USER_NICKNAME_REGISTER_EXIST);
        //确认手机号未被注册,接收用户信息，对密码、手机号码、邮箱进行进行正则校验
        boolean isPhone = RegexUtils.isPhoneInvalid(userInfoDTO.getPhone());
        boolean isPassword = RegexUtils.isPasswordInvalid(userInfoDTO.getPwd());
        boolean isEmail = RegexUtils.isEmailInvalid(userInfoDTO.getEmail());
        if (!isPhone) {
            return Result.fail(UserConstants.USER_PHONE_FORMAT_ERROR);
        }
        if (!isPassword) {
            return Result.fail(UserConstants.USER_PASSWORD_FORMAT_ERROR);
        }

        if (!StrUtil.isEmpty(userInfoDTO.getEmail())) {
            if (!isEmail) {
                return Result.fail(UserConstants.USER_EMAIL_FORMAT_ERROR);
            }
        }
        //将UserInfo转换为Login
        Login userLogin = BeanUtil.copyProperties(userInfoDTO, Login.class);
        //手动事务回滚，因为这个不能进行全局事务设置，因为同时提交的话，无法从Login表取出userId
        try {
            //校验完毕，将登录信息存入登录表
            loginService.save(userLogin);
            //存储完毕，将对应的userId取出
            Long userId = loginService.lambdaQuery()
                    .eq(Login::getPhone, userInfoDTO.getPhone())
                    .one().getUserId();
            //将userId赋值给userInfo
            UserInfo userInfo = BeanUtil.copyProperties(userInfoDTO, UserInfo.class);
            userInfo.setUserId(userId);
            //详细信息存入用户信息表
            save(userInfo);
        } catch (Exception e) {
            //回滚
            loginService.remove(new LambdaQueryWrapper<Login>()
                    .eq(Login::getPhone, userInfoDTO.getPhone()));
            return Result.fail(UserConstants.USER_REGISTER_FAIL);
        }
        return Result.ok(UserConstants.USER_REGISTER_SUCCESS);
    }


    /**
     * 登录状态下查询用户的详细信息
     *
     * @return Result
     */
    @Override
    public Result getDetails() {
        Long userId = UserHolder.getUser().getUserId();
        // 先从redis中查询
        UserInfo userInfoByRedis = JSONUtil.toBean(stringRedisTemplate.opsForValue()
                .get(UserConstants.USER_PHONE + userId), UserInfo.class);
        // 判断是否查询到,如果有，则直接返回
        if (!BeanUtil.isEmpty(userInfoByRedis)) {
            //刷新过期时间
            stringRedisTemplate.expire(UserConstants.USER_PHONE + userId,
                    RedisExpire.USER_INFO_EXPIRE, TimeUnit.SECONDS);
            return Result.ok(userInfoByRedis);
        }
        //从数据库查询
        UserInfo userInfo = getUserInfo(userId);
        if (BeanUtil.isEmpty(userInfo)) {
            return Result.fail(UserConstants.USER_ALREADY_DELETE);
        }
        //将用户信息转换为JSON
        String userInfoJson = JSONUtil.toJsonStr(userInfo);
        // 将用户信息存入redis
        stringRedisTemplate
                .opsForValue()
                .set(UserConstants.USER_PHONE + userId, userInfoJson);
        // 设置过期时间
        stringRedisTemplate
                .expire(UserConstants.USER_PHONE + userId,
                        RedisExpire.USER_INFO_EXPIRE, TimeUnit.SECONDS);
        return Result.ok(userInfo);
    }

    //这里前端只需要传出需要更改的内容，不修改的内容没必要传输，增加SQL写入的负担
    @Override
    public Result modifyUserInfo(UserInfoModifyDTO userInfoModifyDTO) {
        Long userId = UserHolder.getUser().getUserId();
        UserInfo userInfo = BeanUtil.toBean(userInfoModifyDTO, UserInfo.class);
        userInfo.setUserId(userId);
        boolean isUpdate = updateById(userInfo);
        if (!isUpdate) {
            return Result.fail(UserConstants.USER_MODIFY_FAIL);
        }
        return Result.ok(UserConstants.USER_MODIFY_SUCCESS);
    }

    @Override
    public Result deleteUser(HttpServletRequest request) {
        Long userId = UserHolder.getUser().getUserId();
        // 先查询用户信息
        UserInfo userInfo = getUserInfo(userId);
        // 判断是否查询到,为空的话直接返回
        if (BeanUtil.isEmpty(userInfo)) return Result
                .fail(UserConstants.USER_ALREADY_DELETE);
        // 将用户信息转换为转化为逻辑删除对象
        UserInfoLogic userInfoLogic = BeanUtil.toBean(userInfo, UserInfoLogic.class);
        //使用逻辑删除工具类进行逻辑删除
        LogicDeleteUtils.logicDelete(userId, userInfoLogic,
                userInfoLogicService, this, loginService);
        loginService.logout(request);
        return Result.ok(UserConstants.USER_DELETE_SUCCESS);
    }

    @Override
    public Result updatePassword(PasswordEditDTO userPassword) {
        //获取用户id
        Long userId = UserHolder.getUser().getUserId();
        //判断旧密码是否正确
        if (!userPassword.getOldPassword()
                .equals(loginService.lambdaQuery()
                        .eq(Login::getUserId, userId)
                        .one().getPwd())) {
            return Result.fail(UserConstants.OLD_PASSWORD_ERROR);
        }
        //修改密码
        Login login = new Login();
        login.setUserId(userId);
        login.setPwd(userPassword.getNewPassword());
        loginService.updateById(login);
        return Result.ok(UserConstants.MODIFY_PASSWORD_SUCCESS);
    }

    @Override
    public Result forgetPassword(PasswordEditDTO userPassword) {
        //手机号是否为空由前端校验
        //获取手机号
        String phone = userPassword.getPhone();
        //获取校验码
        String code = stringRedisTemplate.opsForValue()
                .get(UserConstants.USER_PHONE + phone);
        //判断验证码是否过期,取到说明未过期,为空就是过期
        if (StrUtil.isBlank(code)) {
            return Result.fail(UserConstants.CODE_EXPIRE);
        }
        //判断验证码是否正确
        if (!code.equals(userPassword.getCode())) {
            return Result.fail(UserConstants.CODE_ERROR);
        }
        //验证码无误，修改用户密码
        boolean update = loginService.update(new LambdaUpdateWrapper<Login>()
                .eq(Login::getPhone, phone)
                .set(Login::getPwd, userPassword.getNewPassword()));
        //判断是否修改成功
        if (!update) {
            return Result.fail(UserConstants.MODIFY_PASSWORD_FAIL);
        }
        return Result.ok(UserConstants.MODIFY_PASSWORD_SUCCESS);
    }


    //由于是已登录状态，所以可以直接通过UserHolder.getUser()获取用户信息，
    // 并且必然存在用户信息，不然无法登录
    private UserInfo getUserInfo(Long userId) {
        //没有就查询数据库
        Login userLogin = loginService
                .lambdaQuery()
                .eq(Login::getUserId, userId)
                .one();
        if (BeanUtil.isEmpty(userLogin)) {
            return null;
        }
        // 根据用户ID查询用户详细信息，这里查出的信息是不存在登录表的信息的
        UserInfo userInfo = lambdaQuery()
                .eq(UserInfo::getUserId, userLogin.getUserId())
                .one();
        //将登录信息复制到用户信息，进行统一封装返回。
        BeanUtil.copyProperties(userLogin, userInfo);
        return userInfo;
    }
}

